﻿using EOM.TSHotelManagement.Common.Core;
using EOM.TSHotelManagement.EntityFramework;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly string _softwareVersion;

    public RequestLoggingMiddleware(
        RequestDelegate next,
        IConfiguration config)
    {
        _next = next;
        _softwareVersion = config["SoftwareVersion"] ?? GetDefaultVersion();
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var startTime = Stopwatch.GetTimestamp();
        Exception exception = null;
        var responseSize = 0L;
        string requestParameters = null;
        RequestLog log = null;

        context.Request.EnableBuffering();

        try
        {
            requestParameters = await GetRequestParametersAsync(context.Request);
        }
        catch (Exception ex)
        {
            var logger = context.RequestServices.GetRequiredService<ILogger<RequestLoggingMiddleware>>();
            logger.LogError(ex, "读取请求参数失败");
        }

        log = new RequestLog
        {
            Path = context.Request.Path,
            Method = context.Request.Method,
            ClientIP = context.Connection.RemoteIpAddress?.ToString(),
            Parameters = requestParameters,
            UserAgent = context.Request.Headers["User-Agent"].ToString(),
            UserName = context.User.Identity?.Name ?? "Anonymous",
            DataInsUsr = context.User.Identity?.Name ?? "Anonymous",
            DataInsDate = DateTime.Now,
            SoftwareVersion = _softwareVersion
        };

        var originalBodyStream = context.Response.Body;

        try
        {
            using (var responseBodyStream = new MemoryStream())
            {
                context.Response.Body = responseBodyStream;

                await _next(context);

                log.ElapsedMilliseconds = GetElapsedMilliseconds(startTime, Stopwatch.GetTimestamp());

                responseBodyStream.Seek(0, SeekOrigin.Begin);
                var responseContent = await new StreamReader(responseBodyStream).ReadToEndAsync();
                responseSize = Encoding.UTF8.GetByteCount(responseContent);

                responseBodyStream.Seek(0, SeekOrigin.Begin);
                await responseBodyStream.CopyToAsync(originalBodyStream);
            }
        }
        catch (Exception ex)
        {
            exception = ex;
            log.Exception = ex.ToString();
            log.StatusCode = StatusCodes.Status500InternalServerError;
            context.Response.StatusCode = log.StatusCode;

            await HandleExceptionAsync(context, ex);
        }
        finally
        {
            context.Response.Body = originalBodyStream;

            log.StatusCode = context.Response.StatusCode;
            log.ResponseSize = responseSize;
            log.ActionName = context.GetEndpoint()?.DisplayName;
            log.DataChgUsr = context.User.Identity?.Name ?? "Anonymous";
            log.DataChgDate = DateTime.Now;

            try
            {
                var repository = context.RequestServices.GetRequiredService<GenericRepository<RequestLog>>();
                await repository.InsertAsync(log);
            }
            catch (Exception ex)
            {
                var logger = context.RequestServices.GetRequiredService<ILogger<RequestLoggingMiddleware>>();
                logger.LogError(ex, "日志记录失败");
            }
        }
    }

    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        context.Response.ContentType = "application/json";
        var errorResponse = new { error = exception.Message };
        await context.Response.WriteAsync(JsonSerializer.Serialize(errorResponse));
    }

    private static long GetElapsedMilliseconds(long start, long stop)
    {
        return (long)((stop - start) * 1000 / (double)Stopwatch.Frequency);
    }

    private string GetDefaultVersion()
    {
        return GetType().Assembly.GetName().Version?.ToString(3) ?? "1.0.0";
    }

    private async Task<string> GetRequestParametersAsync(HttpRequest request)
    {
        if (request.ContentLength == null || request.ContentLength == 0)
            return null;

        try
        {
            request.Body.Seek(0, SeekOrigin.Begin);
            using var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true);
            var parameters = await reader.ReadToEndAsync();
            request.Body.Seek(0, SeekOrigin.Begin);

            return parameters.Length > 8000 ? parameters.Substring(0, 8000) : parameters;
        }
        catch
        {
            return "Unable to read request body";
        }
    }
}